##################################################################
# File : /lua/ui/uimain.lua
# Description : Main UI entry point
# Author : GPG / Neruz
##################################################################

local UIUtil = import('uiutil.lua')
local UIFile = UIUtil.UIFile
local Prefs = import('/lua/user/prefs.lua')
local Text = import('/lua/maui/text.lua').Text
local OnlineProvider = import('/lua/multiplayer/onlineprovider.lua')

local console = false
local firstRun = true

function InitGlobals()
    # Only needs to run once to set up the globals
    UIUtil.SetLayout('minimal')
    UIUtil.SetCurrentSkin('default')

    UIUtil.consoleDepth = 5000000 # no UI element should depth higher than this!
end

#* Initialize the UI states, this is always called on startup
function SetupUI()

    # SetCursor needs to happen anytime this function is called because we
    # could be switching lua states.
    local c = UIUtil.CreateCursor()
    SetCursor( c )

    if firstRun then
        InitGlobals()
        firstRun = false
    end
end

# THE FOLLOWING FUNCTIONS SHOULD NOT BE CALLED FROM LUA CODE
# THEY ARE CALLED FROM THE ENGINE AND EXPECT A DIFFERENT LUA STATE
function StartSplashScreen()
    console = false
    import('/lua/ui/splash/splash.lua').CreateUI()
end

function StartFrontEndUI()
    console = false
    # make sure cheat keys are disabled if needed
    if not DebugFacilitiesEnabled() then
        local keyMap = import('/lua/keymap/DefaultKeyMap.lua')
        IN_RemoveKeyMapTable(keyMap.debugKeyMap)
    end

    import('/lua/ui/menus/loginscreen.lua').CreateUI()
    
end

local function CreateLobby(protocol, port, playerName, natTraversalProvider)
#     local lobby = import('/lua/ui/lobby/lobby.lua')
#     #local lobby = import('/lua/ui/lobby/lobby_v2.lua')
#     return lobby.Lobby(protocol, port, playerName, nil, natTraversalProvider, GetFrame(0), LaunchSession, StartFrontEndUI)
end

#Used by command line to host a game
function StartHostLobbyUI(protocol, port, playerName, gameName, mapName, natTraversalProvider)
    CreateLobby(protocol, port, playerName, natTraversalProvider):HostGame(gameName, mapName, false)
end

#Used by command line to join a game
function StartJoinLobbyUI(protocol, address, playerName, natTraversalProvider)
    CreateLobby(protocol, 0, playerName, nil, natTraversalProvider, GetFrame(0), StartFrontEndUI):JoinGame(address, false)
end

function StartGameUI()
    console = false
    import('/lua/ui/game/gamemain.lua').CreateWldUIProvider()

    # StartGameUI gets called twice when loading a session. The first
    # time is a preload that doesn't initialize UserSync. So we wait
    # until that is defined to do our low level init stuff.
    if GlobalExists("SyncCallback") then
        import('/lua/user/UserCamera.lua').Init()
    end
end
# END SHOULD NOT BE CALLED FROM LUA CODE

# toggle the console window (create if needed)
function ToggleConsole()
    if console then
        if console:IsHidden() then
            console:Show()
        else
            console:Hide()
        end
    else
        console = import('/lua/ui/dialogs/console.lua').CreateDialog()
        console:Show()
    end
end

#* The following scripts are alternate entry points to the UI from the engine
#* Typically these are dialog popup type calls

# context sensitive exit dialog
function ShowEscapeDialog(yesNoOnly)
    import('/lua/ui/dialogs/exitdialog.lua').CreateDialog(yesNoOnly)
end

# structure for tracking custom ESC handlers
# items are organized based on a priority scheme
#   - items of higher priority (lower number) will be closed first
#   - if multiple items exist in a priority bucket, all items of that priority are closed
CustomEscHandlers = {

    # tracks all ESC handlers based on priority
    tblHandlers = {},

    # handles inserting items to this structure based on priority
    AddHandler = function(self, handler, priority)
        # early out if no handler was given
        if not handler then
            return
        end

        # handlers have to be functions
        if type(handler) ~= "function" then
            return
        end

        # if no priority given, assume really low priority
        if not priority then
            priority = 999
        end

        # see if should create this priority bucket
        if not self.tblHandlers[ priority ] then
            self.tblHandlers[ priority ] = {}
        end

        table.insert(self.tblHandlers[ priority ], handler)
    end,

    # remove a particular item from the handlers
    RemoveHandler = function(self, handler, priority)
        # early out if no handler was given
        if not handler then
            return
        end

        # if we are provided a priority, look in that bucket for the handler
        if priority then
            local priBucket = self.tblHandlers[ priority ]
            for key, handlerFunc in pairs(priBucket) do
                if handlerFunc == handler then
                    # found our method, clear it from that list
                    table.remove(priBucket, key)
                end
            end

            # see if we should also clear this priority bucket
            if table.getsize(priBucket) == 0 then
                self.tblHandlers[ priority ] = nil
            end

        # otherwise, search through the entire bucket looking for all occurrences of this handler
        else
            for priority, handlerList in pairs(self.tblHandlers) do
                # loop through the handlers looking for a match
                for key, handlerFunc in ipairs(handlerList) do
                    if handlerFunc == handler then
                        table.remove(handlerList, key)
                    end
                end

                # see if we should also clear this priority bucket
                if table.getn(handlerList) == 0 then
                    self.tblHandlers[ priority ] = nil
                end
            end
        end
    end,

    # executes the handlers per priority and one pri level at a time
    ExecuteHandlers = function(self)
        if table.getsize(self.tblHandlers) == 0 then
            # make sure the info panel and selection is cleared
            SelectUnits({})

            return
        end

        # start looping; however, we will exit once we clear the first priority we hit
        for priority, handlerList in pairs(self.tblHandlers) do
            # run through each handler in the requested priority bucket
            for _, handlerFunc in pairs(handlerList) do
                handlerFunc()
            end

            # clear out this bucket
            self.tblHandlers[ priority ] = nil

            # we processed this priority level. we're done until the user presses ESC again
            break
        end
    end,
}

# interface for creating an ESC handler for methods outside of this file
function AddEscapeHandler(handler, priority)
    CustomEscHandlers:AddHandler(handler, priority)
end

# interface for removing a given ESC handler
function RemoveEscapeHandler(handler, priority)
    CustomEscHandlers:RemoveHandler(handler, priority)
end

# called by the engine when escape is pressed
function EscapeHandler()
    # make sure we do nothing if the World isn't ready for input
    if not WorldIsLoading() then
        # execute all custom handlers for this priority
        CustomEscHandlers:ExecuteHandlers()
    end
end

# network disconnection/boot dialog
local prevDisconnectModule
function UpdateDisconnectDialog()
    local module = import('/lua/ui/dialogs/disconnect.lua')
    if prevDisconnectModule and prevDisconnectModule != module then
        pcall(prevDisconnectModule.DestroyDialog)
    end
    prevDisconnectModule = module
    module.Update()
end

function NoteGameSpeedChanged(clientIndex, newSpeed)
    local clients = GetSessionClients()
    local client = clients[clientIndex]
    # Note: this string has an Engine loc tag because it was
    # originally in the engine.  If we were not already past the loc
    # deadline, I'd change to to be some UI loc tag.  But we are, so
    # I'm not going to change it and risk the wrath of the producers.
    print(LOCF("<LOC Engine0006>%s: adjusting game speed to %+d", client.name, newSpeed))
#    if import('/lua/ui/game/gamemain.lua').GetReplayState() then
#        import('/lua/ui/game/replay.lua').AdjustGameSpeed(newSpeed)
#    end
end

function NoteGameOver()
end


# network desync info
function ShowDesyncDialog(beatNumber, strings)
    import('/lua/ui/dialogs/desync.lua').UpdateDialog(beatNumber, strings)
end

#* The following functions are invoked by the engine to show some text at a certain location
#TODO keep a table of cursor texts to have multiple available?
local cursorText = false

function StartCursorText(x, y, text, color, time, flash)
    if cursorText and cursorText._inTimer then return end # if cursor text has a time, don't stomp it
    if cursorText then cursorText:Destroy() end
    cursorText = Text(GetFrame(0))
    cursorText:SetText(LOC(text))
    cursorText:SetColor(color)
    cursorText:SetFont(UIUtil.bodyFont, 18)
    cursorText:SetDropShadow(true)
    cursorText.Left:Set(x + 16)
    cursorText.Top:Set(y)

    cursorText:SetNeedsFrameUpdate(true)

    cursorText.OnFrame = function(self, elapsedTime)
        if flash then
            if not cursorText._flashTime then
                cursorText._flashTime = 0
            else
                cursorText._flashTime = cursorText._flashTime + elapsedTime
            end
            if cursorText._flashTime > 0.25 then # 1/4 second flashes
                cursorText:SetHidden(not cursorText:IsHidden())
                cursorText._flashTime = 0
            end
        end

        if not cursorText._time then
            cursorText._time = 0
        else
            cursorText._time = cursorText._time + elapsedTime
        end
        if time > 0 then
            cursorText._inTimer = true
            if time < (cursorText._time) then
                cursorText:Destroy()
                cursorText = nil
            end
        end
    end
end

function StopCursorText()
    if cursorText and cursorText._inTimer then return end # if cursor text has a time, don't stomp it
    if cursorText then cursorText:Destroy() end
end

function OnMouseButtonPress(event)
end
